﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;

namespace ZXW.DotNet.Common.Basic.Network.Tcp
{
    public class ComClient
    {
        
        #region 字段

        /// <summary>
        /// 客户端与服务器之间的会话类
        /// </summary>
        private Session _session;

        /// <summary>
        /// 客户端是否已经连接服务器
        /// </summary>
        private bool _isConnected = false;

        /// <summary>
        /// 接收数据缓冲区大小64K
        /// </summary>
        public const int DefaultBufferSize = 5 * 1024;

        /// <summary>
        /// 接收数据缓冲区
        /// </summary>
        private byte[] _recvDataBuffer = new byte[DefaultBufferSize];

        private bool _sendBusy;

        #endregion

        #region 事件

        /// <summary> 
        /// 已经连接服务器事件
        /// </summary>
        public event GenericHandler<object, CommunicationEventArgs> ConnectedServer;
        
        /// <summary>
        /// 接收到数据报文事件
        /// </summary>
        public event GenericHandler<object, CommunicationEventArgs> ReceivedData;

        /// <summary>
        /// 连接断开事件
        /// </summary>
        public event GenericHandler<object, CommunicationEventArgs> DisConnectedServer;

        private void OnConnectedServer(CommunicationEventArgs e)
        {
            if (ConnectedServer != null)
                ConnectedServer(this, e);

        }

        private void OnReceivedData(CommunicationEventArgs e)
        {
            if ( ReceivedData!=null)
                ReceivedData(this,e);
        }
       
        private void OnDisConnectedServer(CommunicationEventArgs e)
        {
            if (DisConnectedServer!=null)
                DisConnectedServer (this,e);
        }
        #endregion

        #region 属性

        /// <summary>
        /// 返回客户端与服务器之间的会话对象
        /// </summary>
        public Session ClientSession
        {
            get
            {
                return _session;
            }
        }

        /// <summary>
        /// 返回客户端与服务器之间的连接状态
        /// </summary>
        public bool IsConnected
        {
            get
            {
                return _isConnected;
            }
        }

        public bool SendBusy
        {
            get
            {
                return _sendBusy;
            }
        }

        #endregion

        #region 公有方法

        public ComClient() { }

        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="ip">服务器IP地址</param>
        /// <param name="port">服务器端口</param>
        public virtual void Connect(string ip, int port)
        {
            if (IsConnected)
                this.Close();            
            Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            newsock.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.ReuseAddress, true);
            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), port);
            newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);
        }

        public virtual void Send(byte[] data)
        {
            if (!_isConnected)
            {
                throw (new ApplicationException("没有连接服务器，不能发送数据"));
            }
            _sendBusy = true;
            _session.ClientSocket.BeginSend(data, 0, data.Length, SocketFlags.None,
             new AsyncCallback(SendDataEnd), _session.ClientSocket);
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public virtual void Close()
        {
            if (!_isConnected)
            {
                return;
            }
            _session.Close();
            _session = null;
            _isConnected = false;
        }

        #endregion

        #region 受保护方法

        /// <summary>
        /// 数据发送完成处理函数
        /// </summary>
        /// <param name="iar"></param>
        protected virtual void SendDataEnd(IAsyncResult iar)
        {
            Socket remote = (Socket)iar.AsyncState;
            int sent = remote.EndSend(iar);
            _sendBusy = false;
            Debug.Assert(sent != 0);
        }

        /// <summary>
        /// 建立Tcp连接后处理过程
        /// </summary>
        /// <param name="iar">异步Socket</param>
        protected virtual void Connected(IAsyncResult iar)
        {
            Socket socket = (Socket)iar.AsyncState;
            try
            {
                socket.EndConnect(iar);
                //创建新的会话
                _session = new Session(socket);

                _isConnected = true;
                //触发连接建立事件
                OnConnectedServer(new CommunicationEventArgs(_session));
                //建立连接后应该立即接收数据
                _session.ClientSocket.BeginReceive(_recvDataBuffer, 0,
                 DefaultBufferSize, SocketFlags.None,
                 new AsyncCallback(RecvData), socket);
            }
            catch 
            {
                OnConnectedServer(new CommunicationEventArgs(null));
                _isConnected = false;
                
            }
        }

        /// <summary>
        /// 数据接收处理函数
        /// </summary>
        /// <param name="iar">异步Socket</param>
        protected virtual void RecvData(IAsyncResult iar)
        {
            Socket remote = (Socket)iar.AsyncState;
            try
            {
                int recv = remote.EndReceive(iar);
                //正常的退出
                if (recv == 0)
                {
                    _session.TypeOfExit = ExitType.NormalExit;
                    OnDisConnectedServer(new CommunicationEventArgs(_session));
                    return;
                }

                //通过事件发布收到的报文
                if (ReceivedData != null)
                {
                    ICloneable copySession = (ICloneable)_session;
                    Session clientSession = (Session)copySession.Clone();
                    byte[] data = new byte[recv];
                    Array.Copy(_recvDataBuffer, 0, data, 0, recv);
                    clientSession.Data = data;
                    OnReceivedData(new CommunicationEventArgs(clientSession));
                }

                //继续接收数据
                if (_session != null)
                    _session.ClientSocket.BeginReceive(_recvDataBuffer, 0, DefaultBufferSize, SocketFlags.None,
                     new AsyncCallback(RecvData), _session.ClientSocket);
            }
            catch (SocketException ex)
            {
                //客户端退出
                if (10054 == ex.ErrorCode)
                {
                    //服务器强制的关闭连接，强制退出
                    _session.TypeOfExit = ExitType.ExceptionExit;
                    _isConnected = false;
                    OnDisConnectedServer(new CommunicationEventArgs(_session));
                }
                else
                {
                    //throw (ex);
                }
            }
            catch (ObjectDisposedException ex)
            {
                if (ex != null)
                {
                    ex = null;
                }
            }
        }
        #endregion
    }
}
